package nature.nano.util.provider;

import nature.nano.component.PrimaryProvider;
import nature.nano.component.EmptyProps;
import nature.nano.component.SimpleProps;
import nature.nano.component.Strings;
import nature.nano.control.script.run.MetaRunner;
import nature.nano.iface.Accepter;
import nature.nano.iface.Pipe;
import nature.nano.iface.Props;
import nature.nano.iface.Provider;
import nature.nano.iface.Storer;
import nature.nano.iface.enumer.EnumStringCallback;
import nature.nano.iface.secondary.Configable;
import nature.nano.iface.secondary.SlotBoard;
import nature.nano.iface.secondary.Startable;
import nature.nano.iface.secondary.Stoppable;
import nature.nano.classinfo.ClassInfoLib;

public class ClassInfo extends PrimaryProvider implements Accepter {

	private StringBuilder clazzCache = new StringBuilder();
	
	public ClassInfo() {
		props(Strings.clazz);
	}
	
	@Override
	public void start() {
		output().onBegin(Strings.root, EmptyProps.instance);
		error().onBegin(Strings.root, EmptyProps.instance);
		
		String clazz = prop(Strings.clazz);
		if (clazz == null) {
			error(Strings.error, Strings.errorType, Strings.requireProp, Strings.prop, Strings.clazz);
		} else {
			String[] list = clazz.split(Strings.regSeparator);
			for (String it : list) {
				flushClazz(it.trim());
			}
		}
		
		error().onEnd(Strings.root);
		output().onEnd(Strings.root);
	}
	
	private void flushClazz(String clazz) {
		Object object = ClassInfoLib.instance().newInstance(clazz);
		if (object == null) {
			error(Strings.error, Strings.errorType, Strings.cantCreateInstance, Strings.clazz, clazz);
			return;
		}		
		
		SimpleProps props = new SimpleProps();
		flushInfo(clazz, object, props);
		output().onBegin(Strings.clazz, props);
		output().onEnd(Strings.clazz);
	}
	
	private void flushInfo(String clazz, Object object, SimpleProps props) {
		props.add(Strings.clazz, clazz);
		props.add(Strings.fullName, object.getClass().getName());
		
		flushSlots(object, props);
		flushProps(object, props);
		
		props.add("is-accepter", Boolean.toString(object instanceof Accepter));
		props.add("is-provider", Boolean.toString(object instanceof Provider));
		props.add("is-pipe", Boolean.toString(object instanceof Pipe));
		props.add("is-storer", Boolean.toString(object instanceof Storer));
		props.add("is-slotboard", Boolean.toString(object instanceof SlotBoard));
		props.add("is-configable", Boolean.toString(object instanceof Configable));
		props.add("is-startable", Boolean.toString(object instanceof Startable));
		props.add("is-stoppable", Boolean.toString(object instanceof Stoppable));			
	}
	
	private void flushSlots(Object object, SimpleProps props) {
		if (!(object instanceof SlotBoard)) {
			return;
		}
		SlotBoard board = (SlotBoard) object;

		final StringBuilder slots = new StringBuilder();
		board.enumSlot(
			new EnumStringCallback() {
				public void onRead(String slot) {
					slots.append(slot);
					slots.append(Strings.separator);
				}
			});
		if (slots.length() > 0) {
			slots.deleteCharAt(slots.length() - 1);
		}
		
		props.add(Strings.slots, slots.toString());
	}

	private void flushProps(Object object, SimpleProps props) {
		if (!(object instanceof Configable)) {
			return;
		}
		Configable configable = (Configable) object;

		final StringBuilder builder = new StringBuilder();
		configable.enumProp(
			new EnumStringCallback() {
				public void onRead(String prop) {
					builder.append(prop);
					builder.append(Strings.separator);
				}
			});
		if (builder.length() > 0) {
			builder.deleteCharAt(builder.length() - 1);
		}
		
		props.add(Strings.props, builder.toString());
	}

	@Override
	public void onBegin(String type, Props props) {
		if (type.equals(Strings.clazz)) {
			String name = props.get(Strings.clazz);
			if (name != null) {
				clazzCache.append(name);
				clazzCache.append(Strings.separator);
			}
		}
	}

	@Override
	public void onContent(String content) {
	}

	@Override
	public void onEnd(String type) {
		if (type.equals(Strings.root)) {
			setProp(Strings.clazz, clazzCache.toString());
		}
	}

	public static void main(String[] args) {
		MetaRunner.run("ClassInfo[ClassLibrary] > Printer");
		MetaRunner.run("ClassLibrary > ClassInfo.error > Printer");
	}
	
}
